“Conceitos Estatísticos para IA”
Prof. Adelaide Alves de Oliveira
- TURMA: FIAP-06IA
- EDUARDO MORARES
- EDUARDO SIQUEIRA DE LIMA
- GABRIEL SHIKAMA
- RICARDO KALIMANIS
Inicicalmente vamos instalar os pacotes, caso seja necessário, que serão usados no decorrer da análise
#ignorando alertas para não poluir a exibição do markdwon
options(warn =-1)
#lista de pacotes que iremos utilizar no projeto
Pacotes_Necessarios <- c("ggplot2","readr","dplyr", "corrgram","corrplot", "plotly","skimr","gridExtra","GGally","ggpubr")
#com base nos pacotes instalados crio uma variavel somente com os pacotes que não temos ainda para realizar a instalação
#dos pacotes que de fato não possuimos
PacotesNovos <- Pacotes_Necessarios[!(Pacotes_Necessarios %in% installed.packages()[,"Package"])]
if(length(PacotesNovos)){ install.packages(PacotesNovos)} else {print("Todos os Pacotes Estão Instalados")}
[1] "Todos os Pacotes Estão Instalados"
Carregando todas as as Bibliotecas mencionadas no passo anterior
lapply(Pacotes_Necessarios, require, character.only = TRUE)
Importando os Datasets de Wines Quality.
Lista DE - PARA das colunas
| ID |
ID (que não sera utilizado) |
| fixed acidity |
acidez_fixa |
| volatile acidity |
acidez_volatil |
| citric acid |
acido_citrico |
| residual sugar |
acucar_residual |
| chlorides |
cloretos |
| free sulfur dioxide |
fsd |
| total sulfur dioxide |
tsd |
| density |
densidade |
| pH |
PH |
| sulphates |
sulfatos |
| alcohol |
grau_alcolico |
| quality |
qualidade |
| Vinho |
Tipo |
#Criando uma variável nome_colunas que receberá os nomes das colunas que normalizaremos a fim de facilitar o resto da análise
nome_colunas <- c("id","acidez_fixa","acidez_volatil","acido_citrico","acucar_residual","cloretos", "fsd", "tsd","densidade","PH", "sulfatos","grau_alcolico","qualidade","tipo")
#uso da biblioteca readr é para obter uma performance de carga melhor que a lib padrão do R
#e escolhemos o read_csv2 justamente pelo fato do arquivo estar separado por ; ao invés de ,
#o separador decimal também não é o . que é convencional e este comando ja os converte facilmente
#skip = 1 para ignorar o cabecalho que mudamos para melhor entendimento
vinhos <- read_csv2("./DataSets/BaseWine_Red_e_White.csv" ,col_names = nome_colunas, skip = 1)
Adicionando uma classificação de bom ou ruim de acordo com sua nota, onde menor que 5 é ruim até 7 bom e acima disso excelente
#converte tipo paara fator
vinhos$tipo <- factor(vinhos$tipo, ordered = T)
#Criando uma coluna de Ranking para dizer o quão bom é o vinho
vinhos$rating <- factor(ifelse(vinhos$qualidade <= 5, 'Ruim', ifelse(vinhos$qualidade <= 7, 'Bom', 'Excelente')))
Exibindo os dados das dimensões, sumário, estrutura e as primeiras linhas do DataFrame Vinhos
attach(vinhos)
skim(vinhos[, names(vinhos) != "id"] )
Skim summary statistics
n obs: 6497
n variables: 14
-- Variable type:factor --------------------------------------------------------
variable missing complete n n_unique top_counts ordered
rating 0 6497 6497 3 Bom: 3915, Rui: 2384, Exc: 198, NA: 0 FALSE
tipo 0 6497 6497 2 WHI: 4898, RED: 1599, NA: 0 TRUE
-- Variable type:numeric -------------------------------------------------------
variable missing complete n mean sd p0 p25 p50 p75 p100 hist
acidez_fixa 0 6497 6497 7.22 1.3 3.8 6.4 7 7.7 15.9 ▁▇▇▂▁▁▁▁
acidez_volatil 0 6497 6497 0.34 0.16 0.08 0.23 0.29 0.4 1.58 ▇▇▂▁▁▁▁▁
acido_citrico 0 6497 6497 0.32 0.15 0 0.25 0.31 0.39 1.66 ▂▇▂▁▁▁▁▁
acucar_residual 0 6497 6497 5.44 4.73 0.6 1.8 3 8.1 45.8 ▇▂▂▁▁▁▁▁
cloretos 0 6497 6497 0.056 0.035 0.009 0.038 0.047 0.065 0.61 ▇▁▁▁▁▁▁▁
densidade 0 6497 6497 0.99 0.003 0.99 0.99 0.99 1 1.01 ▂▇▇▅▁▁▁▁
fsd 0 6497 6497 30.53 17.75 1 17 29 41 289 ▇▃▁▁▁▁▁▁
grau_alcolico 0 6497 6497 10.49 1.22 0.96 9.5 10.3 11.3 14.9 ▁▁▁▁▆▇▃▁
PH 0 6497 6497 3.22 0.16 2.72 3.11 3.21 3.32 4.01 ▁▃▇▇▃▁▁▁
qualidade 0 6497 6497 5.82 0.87 3 5 6 6 9 ▁▁▆▇▁▃▁▁
sulfatos 0 6497 6497 0.53 0.15 0.22 0.43 0.51 0.6 2 ▅▇▂▁▁▁▁▁
tsd 0 6497 6497 115.74 56.52 6 77 118 156 440 ▅▆▇▃▁▁▁▁
Observa-se que:
- O campo
acucar_residual e fsd possuem um desvio padrão acima das demais variaveis
- A maioria dos histogramas apresenta uma distribuição normal entretanto não centralizado o que pode indicar a presença de outliers
Verificando valores nulos
sapply(vinhos, function(x)all(is.na(x)))
O resultado acima nos descreve que não há presença de nulos na base, isto é indicado pelo retorno FALSE em cada variável
Rotulos_Colunas <-c("id","acidez_fixa","acidez volatil" ,"acido citrico","acucar residual","cloretos","fsd","tsd","densidade",
"PH","sulfatos","grau alcolico","qualidade","tipo")
grafico_lista <- vector("list", length = length(Rotulos_Colunas)-2)
for(i in 2:13){
grafico_lista[[i-1]] <- plot_ly(x = as.formula(vinhos[i]), type = 'histogram', name = Rotulos_Colunas[i])
}
subplot(grafico_lista, nrows = 4)
NA
Podemos observar que apesar do desenho ser similar à uma distribuição normal, isso se deu pois mais à esquerda exceto grau alcoolico, que se sabe que não há vinhos com teor alcoolico abaixo de 6.5% (levendo em consideração um vinho de sobremesa mais licoroso). portanto nosso proximo objeto de estudo será o grau alcoolico a procura de outliers e a remoção dos mesmos.
p <- function(..., sep = ''){
paste(..., collapse = sep)
}
fn_Exibir_Dispersao <- function (col, columnName){
print(p('O menor elemento de', columnName, ' é', min(col)))
print(p('O maior elemento de', columnName, ' é', max(col)))
print(p('Variação Populacional de', columnName, 'é', var(col), sep = ' '))
print(p('Desvio padrão de', columnName, 'é', sd(col), sep = ' '))
print(p('A média dos valores de ', columnName, 'é', mean(col), sep = ' '))
print(p('A diferença entre a média e a mediana é de', columnName, 'é', abs(mean(col) - sd(col)), sep = ' '))
}
Parece que existem elementos que nos levam a crer que de fato não são condizentes com o minimo de teor alcoolico existente para vinhos iremos analisar melhor as observações onde o teor alcoolico é menor que 6º
Olhando a tabela percebmos que existem 4 observações para vinho tinto que nos parecem ser erros de digitação iremos criar a partir deste momento um novo data frame para trabalhar a limpeza dos dados preservando o original por questões de segurança e em seguida imprimiremos os graficos novamente para a variavel grau alcoolico
vinhos_ajustado <- subset(vinhos, grau_alcolico > 6)
grafico_lista <- vector("list", length = 2)
grafico_lista[[1]] <- plot_ly(x = vinhos_ajustado$grau_alcolico, type = 'histogram', name = 'Histograma - Grau Alcoolico')
grafico_lista[[2]] <- plot_ly(y = vinhos_ajustado$grau_alcolico, type = "box", name = 'Teor Alcoolico')
subplot(grafico_lista, nrows = 1)
NA
Após a remoção do erro na coluna grau_alcoolico os dados se demosntraram melhores dispostos no entanto ainda há indicios que existam outliers que devem ser melhor analisados
O comando aplicado para todos atributos abaixo exibição dos box-plot abaixo foi:
boxplot(Column_Name, col=“slategray2”, pch=19)
mtext(“Titulo” , cex=0.8, side=1, line=2)
Outliers
Nota se que quase todas as variáveis possuem outliers e para uma melhor acurácia no modelo iremos removê-las da análise. O Metodo mais indicado para remoção dos outliers é amplitude interquartil (IQR - InterQuantile Range) onde: *IQR = Q3(quartil 3) - Q1(quartil 1) Com o IQR calculado é necessário definir o limite inferior e superior que é dado pela seguinte formula:
\[Lim_{Sup} = \bar{X} + 1,5 * IQR\]
\[Lim_{Inf} = \bar{X} - 1,5 * IQR\]
Para elucidar a aplicação do método vamos analisar a variavel “Acidez Fixa” notamos que há muitos valores acima do Q3 a uns poucos abaixo de Q1, Oserve o sumário desta variavel
skim(acidez_fixa)
#dado p25(Q1) = 6.4 e p75(Q3) = 7.7 temos:
IQR_Acidez_Volatil =7.7 - 6.4
#como resultado temos 1.3
#com isso podemos definir os limites superior e inferior do range
LS_acidez_fixa = 7.7 + 1.5 * IQR_Acidez_Volatil
LI_acidez_fixa = 6.4 - (1.5 * IQR_Acidez_Volatil)
#temos um limite superior de -0.025 e inferior de 0.655 onde as observações acima ou abaixo desses fatores serão consideradas outilers
#com base nos limites encontrados farei um sub set do conjunto origi nal de dados levando em consideracao os limites encontrados
Vinhos_Normalizado <-filter(vinhos, acidez_fixa >= LI_acidez_fixa & acidez_fixa <= LS_acidez_fixa)
#comparando os resultados
par(mfrow=c(1,2), oma = c(1,1,0,0) + 0.1, mar = c(3,3,1,1) + 0.1)
boxplot(acidez_fixa, col="slategray2", pch=19)
mtext("Antes" , cex=0.8, side=1, line=2)
boxplot(Vinhos_Normalizado$acidez_fixa, col="slategray2", pch=19)
mtext("Depois" , cex=0.8, side=1, line=2)
De modo que a remoção dos outliers fez com que a distribuição dos dados ficasse mais homogênea como demonstro nos histrogramas a seguir
#comparando os resultados
par(mfrow=c(1,2), oma = c(1,1,0,0) + 0.1, mar = c(3,3,1,1) + 0.1)
hist(acidez_fixa, col="slategray2", main = "")
mtext("Antes" , cex=0.8, side=1, line=2)
hist(Vinhos_Normalizado$acidez_fixa, col="slategray2", main = "")
mtext("Depois" , cex=0.8, side=1, line=2)
Para automatizar o processo de analise e remoção dos outliers criamos uma função para executar essa tarefa, e segue:
Examinando os dados pos limpeza dos outliers
LS0tDQp0aXRsZTogIkFOQUxJU0UgRE8gREFUQVNFVCBXSU5FIFFVQUxJVFkiDQpvdXRwdXQ6DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KIyMjICAqKiJDb25jZWl0b3MgRXN0YXTDrXN0aWNvcyBwYXJhIElBIioqICANCiMjIyMgICpQcm9mLiBBZGVsYWlkZSBBbHZlcyBkZSBPbGl2ZWlyYSoNCg0KPGJyLz4NCg0KKiBUVVJNQTogRklBUC0wNklBDQogICsgRURVQVJETyBNT1JBUkVTDQogICsgRURVQVJETyBTSVFVRUlSQSBERSBMSU1BDQogICsgR0FCUklFTCBTSElLQU1BDQogICsgUklDQVJETyBLQUxJTUFOSVMNCg0KPGJyLz48YnIvPg0KSW5pY2ljYWxtZW50ZSB2YW1vcyBpbnN0YWxhciBvcyBwYWNvdGVzLCBjYXNvIHNlamEgbmVjZXNzw6FyaW8sIHF1ZSBzZXLDo28gdXNhZG9zIG5vIGRlY29ycmVyIGRhIGFuw6FsaXNlDQpgYGB7cn0NCg0KI2lnbm9yYW5kbyBhbGVydGFzIHBhcmEgbsOjbyBwb2x1aXIgYSBleGliacOnw6NvIGRvIG1hcmtkd29uDQpvcHRpb25zKHdhcm4gPS0xKQ0KDQojbGlzdGEgZGUgcGFjb3RlcyBxdWUgaXJlbW9zIHV0aWxpemFyIG5vIHByb2pldG8NClBhY290ZXNfTmVjZXNzYXJpb3MgPC0gYygiZ2dwbG90MiIsInJlYWRyIiwiZHBseXIiLCAiY29ycmdyYW0iLCJjb3JycGxvdCIsICJwbG90bHkiLCJza2ltciIsImdyaWRFeHRyYSIsIkdHYWxseSIsImdtb2RlbHMiKQ0KDQojY29tIGJhc2Ugbm9zIHBhY290ZXMgaW5zdGFsYWRvcyBjcmlvIHVtYSB2YXJpYXZlbCBzb21lbnRlIGNvbSBvcyBwYWNvdGVzIHF1ZSBuw6NvIHRlbW9zIGFpbmRhIHBhcmEgcmVhbGl6YXIgYSBpbnN0YWxhw6fDo28gDQojZG9zIHBhY290ZXMgcXVlIGRlIGZhdG8gbsOjbyBwb3NzdWltb3MNClBhY290ZXNOb3ZvcyA8LSBQYWNvdGVzX05lY2Vzc2FyaW9zWyEoUGFjb3Rlc19OZWNlc3NhcmlvcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKV0NCmlmKGxlbmd0aChQYWNvdGVzTm92b3MpKXsgaW5zdGFsbC5wYWNrYWdlcyhQYWNvdGVzTm92b3MpfSBlbHNlIHtwcmludCgiVG9kb3Mgb3MgUGFjb3RlcyBFc3TDo28gSW5zdGFsYWRvcyIpfQ0KDQpgYGANCg0KDQoNCkNhcnJlZ2FuZG8gdG9kYXMgYXMgYXMgQmlibGlvdGVjYXMgbWVuY2lvbmFkYXMgbm8gcGFzc28gYW50ZXJpb3INCmBgYHtyLCByZXN1bHRzPSJoaWRlIn0NCmxhcHBseShQYWNvdGVzX05lY2Vzc2FyaW9zLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KDQoNCkltcG9ydGFuZG8gb3MgRGF0YXNldHMgZGUgV2luZXMgUXVhbGl0eS4NCg0KDQojIyMjIExpc3RhIERFIC0gUEFSQSBkYXMgY29sdW5hcw0KDQoNCg0KTm9tZSBubyBBcnF1aXZvICAgICB8ICBOb21lIFRyYWR1emlkbw0KLS0tLS0tLS0tLS0tLS0tLSAgICB8LS0tLS0tLS0tLS0tLS0tLS0NCklEICAgICAgICAgICAgICAgICAgfElEIChxdWUgbsOjbyBzZXJhIHV0aWxpemFkbykNCmZpeGVkIGFjaWRpdHkgICAgICAgfGFjaWRlel9maXhhDQp2b2xhdGlsZSBhY2lkaXR5ICAgIHxhY2lkZXpfdm9sYXRpbA0KY2l0cmljIGFjaWQgICAgICAgICB8YWNpZG9fY2l0cmljbwkNCnJlc2lkdWFsIHN1Z2FyICAgICAgfGFjdWNhcl9yZXNpZHVhbA0KY2hsb3JpZGVzICAgICAgICAgICB8Y2xvcmV0b3MNCmZyZWUgc3VsZnVyIGRpb3hpZGUgfGZzZA0KdG90YWwgc3VsZnVyIGRpb3hpZGV8dHNkDQpkZW5zaXR5ICAgICAgICAgICAgIHxkZW5zaWRhZGUNCnBIICAgICAgICAgICAgICAgICAgfFBIDQpzdWxwaGF0ZXMgICAgICAgICAgIHxzdWxmYXRvcw0KYWxjb2hvbCAgICAgICAgICAgICB8Z3JhdV9hbGNvbGljbw0KcXVhbGl0eSAgICAgICAgICAgICB8cXVhbGlkYWRlDQpWaW5obyAgICAgICAgICAgICAgIHxUaXBvDQoNCg0KYGBge3IsIHJlc3VsdHM9ImhpZGUifQ0KDQojQ3JpYW5kbyB1bWEgdmFyacOhdmVsIG5vbWVfY29sdW5hcyBxdWUgcmVjZWJlcsOhIG9zIG5vbWVzIGRhcyBjb2x1bmFzIHF1ZSBub3JtYWxpemFyZW1vcyBhIGZpbSBkZSBmYWNpbGl0YXIgbyByZXN0byBkYSBhbsOhbGlzZQ0Kbm9tZV9jb2x1bmFzIDwtIGMoImlkIiwiYWNpZGV6X2ZpeGEiLCJhY2lkZXpfdm9sYXRpbCIsImFjaWRvX2NpdHJpY28iLCJhY3VjYXJfcmVzaWR1YWwiLCJjbG9yZXRvcyIsICJmc2QiLCAidHNkIiwiZGVuc2lkYWRlIiwiUEgiLCAic3VsZmF0b3MiLCJncmF1X2FsY29saWNvIiwicXVhbGlkYWRlIiwidGlwbyIpDQoNCiN1c28gZGEgYmlibGlvdGVjYSByZWFkciDDqSBwYXJhIG9idGVyIHVtYSBwZXJmb3JtYW5jZSBkZSBjYXJnYSBtZWxob3IgcXVlIGEgbGliIHBhZHLDo28gZG8gUg0KI2UgZXNjb2xoZW1vcyBvIHJlYWRfY3N2MiBqdXN0YW1lbnRlIHBlbG8gZmF0byBkbyBhcnF1aXZvIGVzdGFyIHNlcGFyYWRvIHBvciA7IGFvIGludsOpcyBkZSAsDQojbyBzZXBhcmFkb3IgZGVjaW1hbCB0YW1iw6ltIG7Do28gw6kgbyAuIHF1ZSDDqSBjb252ZW5jaW9uYWwgZSBlc3RlIGNvbWFuZG8gamEgb3MgY29udmVydGUgZmFjaWxtZW50ZQ0KI3NraXAgPSAxIHBhcmEgaWdub3JhciBvIGNhYmVjYWxobyBxdWUgbXVkYW1vcyBwYXJhIG1lbGhvciBlbnRlbmRpbWVudG8NCnZpbmhvcyA8LSByZWFkX2NzdjIoIi4vRGF0YVNldHMvQmFzZVdpbmVfUmVkX2VfV2hpdGUuY3N2IiAsY29sX25hbWVzID0gbm9tZV9jb2x1bmFzLCBza2lwID0gMSkNCg0KDQpgYGANCg0KDQpBZGljaW9uYW5kbyB1bWEgY2xhc3NpZmljYcOnw6NvIGRlIGJvbSBvdSBydWltIGRlIGFjb3JkbyBjb20gc3VhIG5vdGEsIG9uZGUgbWVub3IgcXVlIDUgw6kgcnVpbSBhdMOpIDcgYm9tIGUgYWNpbWEgZGlzc28gZXhjZWxlbnRlDQoNCmBgYHtyfQ0KDQojY29udmVydGUgdGlwbyBwYWFyYSBmYXRvcg0KdmluaG9zJHRpcG8gPC0gZmFjdG9yKHZpbmhvcyR0aXBvLCBvcmRlcmVkID0gVCkNCg0KI0NyaWFuZG8gdW1hIGNvbHVuYSBkZSBSYW5raW5nIHBhcmEgZGl6ZXIgbyBxdcOjbyBib20gw6kgbyB2aW5obw0KdmluaG9zJHJhdGluZyA8LSBmYWN0b3IoaWZlbHNlKHZpbmhvcyRxdWFsaWRhZGUgPD0gNSwgJ1J1aW0nLCBpZmVsc2UodmluaG9zJHF1YWxpZGFkZSA8PSA3LCAnQm9tJywgJ0V4Y2VsZW50ZScpKSkNCg0KYGBgDQoNCg0KDQpFeGliaW5kbyBvcyBkYWRvcyBkYXMgZGltZW5zw7Vlcywgc3Vtw6FyaW8sIGVzdHJ1dHVyYSBlIGFzIHByaW1laXJhcyBsaW5oYXMgZG8gRGF0YUZyYW1lIFZpbmhvcw0KDQpgYGB7ciAscmVzdWx0cz0iaGlkZSJ9DQoNCmF0dGFjaCh2aW5ob3MpDQoNCmBgYA0KDQpgYGB7cn0NCiNkZW1vbnN0cmFuZG8gYXMgcXVhbnRpZGFkZXMgZGUgZGltZW5zb2VzIGRlIGFtYm9zIGRhdGFzZXRzDQoNCmRpbSh2aW5ob3MpDQpza2ltKHZpbmhvc1ssIG5hbWVzKHZpbmhvcykgIT0gImlkIl0gKSAjcmV0aXJhbmRvIGEgY29sdW5hIElEIGRhIGFuw6FsaXNlDQpzdHIodmluaG9zKQ0KaGVhZCAodmluaG9zLDMpDQoNCmBgYA0KDQpPYnNlcnZhLXNlIHF1ZToNCg0KKiBPIGNhbXBvIGBhY3VjYXJfcmVzaWR1YWxgIGUgYGZzZGAgcG9zc3VlbSB1bSBkZXN2aW8gcGFkcsOjbyBhY2ltYSBkYXMgZGVtYWlzIHZhcmlhdmVpcw0KKiBBIG1haW9yaWEgZG9zIGhpc3RvZ3JhbWFzIGFwcmVzZW50YSB1bWEgZGlzdHJpYnVpw6fDo28gbm9ybWFsIGVudHJldGFudG8gbsOjbyBjZW50cmFsaXphZG8gbyBxdWUgcG9kZSBpbmRpY2FyIGEgcHJlc2Vuw6dhIGRlIG91dGxpZXJzDQoNCg0KDQojIyMgVmVyaWZpY2FuZG8gdmFsb3JlcyBudWxvcw0KDQpgYGB7cn0NCg0Kc2FwcGx5KHZpbmhvcywgZnVuY3Rpb24oeClhbGwoaXMubmEoeCkpKQ0KDQpgYGANCg0KDQpPIHJlc3VsdGFkbyBhY2ltYSBub3MgZGVzY3JldmUgcXVlIG7Do28gaMOhIHByZXNlbsOnYSBkZSBudWxvcyBuYSBiYXNlLCBpc3RvIMOpIGluZGljYWRvIHBlbG8gcmV0b3JubyBgRkFMU0VgIGVtIGNhZGEgdmFyacOhdmVsDQoNCg0KYGBge3J9DQoNClJvdHVsb3NfQ29sdW5hcyA8LWMoImlkIiwiYWNpZGV6X2ZpeGEiLCJhY2lkZXogdm9sYXRpbCIJLCJhY2lkbyBjaXRyaWNvIiwiYWN1Y2FyIHJlc2lkdWFsIiwiY2xvcmV0b3MiLCJmc2QiLCJ0c2QiLCJkZW5zaWRhZGUiLAkJCQ0KICAgICAgICAgICAgICAgICAgICAiUEgiLCJzdWxmYXRvcyIsImdyYXUgYWxjb2xpY28iLCJxdWFsaWRhZGUiLCJ0aXBvIikNCg0KZ3JhZmljb19saXN0YSA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgoUm90dWxvc19Db2x1bmFzKS0yKQ0KDQpmb3IoaSBpbiAyOjEzKXsNCiAgZ3JhZmljb19saXN0YVtbaS0xXV0gPC0gcGxvdF9seSh4ID0gYXMuZm9ybXVsYSh2aW5ob3NbaV0pLCAgIHR5cGUgPSAnaGlzdG9ncmFtJywgbmFtZSA9IFJvdHVsb3NfQ29sdW5hc1tpXSkNCn0gIA0Kc3VicGxvdChncmFmaWNvX2xpc3RhLCAgbnJvd3MgPSA0KQ0KDQpgYGANCg0KDQoNClBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGFwZXNhciBkbyBkZXNlbmhvIHNlciBzaW1pbGFyIMOgIHVtYSBkaXN0cmlidWnDp8OjbyBub3JtYWwsIGlzc28gc2UgZGV1IHBvaXMgbWFpcyDDoCBlc3F1ZXJkYSBleGNldG8gZ3JhdSBhbGNvb2xpY28sIHF1ZSBzZSBzYWJlDQpxdWUgbsOjbyBow6EgdmluaG9zIGNvbSB0ZW9yIGFsY29vbGljbyBhYmFpeG8gZGUgNi41JSAobGV2ZW5kbyBlbSBjb25zaWRlcmHDp8OjbyB1bSB2aW5obyBkZSBzb2JyZW1lc2EgbWFpcyBsaWNvcm9zbykuIHBvcnRhbnRvIG5vc3NvIHByb3hpbW8gb2JqZXRvIGRlDQplc3R1ZG8gc2Vyw6EgbyBncmF1IGFsY29vbGljbyBhIHByb2N1cmEgZGUgb3V0bGllcnMgZSBhIHJlbW/Dp8OjbyBkb3MgbWVzbW9zLg0KDQoNCmBgYHtyIH0NCnAgPC0gZnVuY3Rpb24oLi4uLCBzZXAgPSAnJyl7DQogIHBhc3RlKC4uLiwgY29sbGFwc2UgPSBzZXApDQp9DQoNCmZuX0V4aWJpcl9EaXNwZXJzYW8gPC0gZnVuY3Rpb24gKGNvbCwgY29sdW1uTmFtZSl7DQogIHByaW50KHAoJ08gbWVub3IgZWxlbWVudG8gZGUnLCBjb2x1bW5OYW1lLCAnIMOpJywgbWluKGNvbCkpKQ0KICBwcmludChwKCdPIG1haW9yIGVsZW1lbnRvIGRlJywgY29sdW1uTmFtZSwgJyDDqScsIG1heChjb2wpKSkNCiAgcHJpbnQocCgnVmFyaWHDp8OjbyBQb3B1bGFjaW9uYWwgZGUnLCBjb2x1bW5OYW1lLCAnw6knLCB2YXIoY29sKSwgc2VwID0gJyAnKSkNCiAgcHJpbnQocCgnRGVzdmlvIHBhZHLDo28gZGUnLCBjb2x1bW5OYW1lLCAnw6knLCBzZChjb2wpLCBzZXAgPSAnICcpKQ0KICBwcmludChwKCdBIG3DqWRpYSBkb3MgdmFsb3JlcyBkZSAnLCBjb2x1bW5OYW1lLCAnw6knLCBtZWFuKGNvbCksIHNlcCA9ICcgJykpDQogIHByaW50KHAoJ0EgZGlmZXJlbsOnYSBlbnRyZSBhIG3DqWRpYSBlIGEgbWVkaWFuYSDDqSBkZScsIGNvbHVtbk5hbWUsICfDqScsIGFicyhtZWFuKGNvbCkgLSBzZChjb2wpKSwgc2VwID0gJyAnKSkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KDQpmbl9FeGliaXJfRGlzcGVyc2FvKHZpbmhvcyRncmF1X2FsY29saWNvLCAnVGVvciBBbGNvb2xpY28nKQ0KDQpwbG90X2x5KHkgPSBncmF1X2FsY29saWNvLCB0eXBlID0gImJveCIsIG5hbWUgPSAnVGVvciBBbGNvb2xpY28nKSANCg0KDQpgYGANCg0KDQpQYXJlY2UgcXVlIGV4aXN0ZW0gZWxlbWVudG9zIHF1ZSBub3MgbGV2YW0gYSBjcmVyIHF1ZSBkZSBmYXRvIG7Do28gc8OjbyBjb25kaXplbnRlcyBjb20gbyBtaW5pbW8gZGUgdGVvciBhbGNvb2xpY28gZXhpc3RlbnRlIHBhcmEgdmluaG9zDQppcmVtb3MgYW5hbGlzYXIgbWVsaG9yIGFzIG9ic2VydmHDp8O1ZXMgb25kZSBvIHRlb3IgYWxjb29saWNvIMOpIG1lbm9yIHF1ZSA2wroNCmBgYCB7cn0NCg0Kc3Vic2V0KHZpbmhvcywgZ3JhdV9hbGNvbGljbyA8NikNCg0KYGBgYA0KDQoNCk9saGFuZG8gYSB0YWJlbGEgcGVyY2VibW9zIHF1ZSBleGlzdGVtIDQgb2JzZXJ2YcOnw7VlcyBwYXJhIHZpbmhvIHRpbnRvIHF1ZSBub3MgcGFyZWNlbSBzZXIgZXJyb3MgZGUgZGlnaXRhw6fDo28gaXJlbW9zIGNyaWFyIGEgcGFydGlyIGRlc3RlIG1vbWVudG8gdW0gbm92bw0KZGF0YSBmcmFtZSBwYXJhIHRyYWJhbGhhciBhIGxpbXBlemEgZG9zIGRhZG9zIHByZXNlcnZhbmRvIG8gb3JpZ2luYWwgcG9yIHF1ZXN0w7VlcyBkZSBzZWd1cmFuw6dhIGUgZW0gc2VndWlkYSBpbXByaW1pcmVtb3Mgb3MgZ3JhZmljb3Mgbm92YW1lbnRlIHBhcmEgYSB2YXJpYXZlbCBncmF1IGFsY29vbGljbw0KDQoNCmBgYCB7cn0NCg0KdmluaG9zX2FqdXN0YWRvIDwtIHN1YnNldCh2aW5ob3MsIGdyYXVfYWxjb2xpY28gPiA2KQ0KDQpncmFmaWNvX2xpc3RhIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IDIpDQoNCmdyYWZpY29fbGlzdGFbWzFdXSA8LSAgcGxvdF9seSh4ID0gdmluaG9zX2FqdXN0YWRvJGdyYXVfYWxjb2xpY28sIHR5cGUgPSAnaGlzdG9ncmFtJywgbmFtZSA9ICdIaXN0b2dyYW1hJykNCmdyYWZpY29fbGlzdGFbWzJdXSA8LSAgcGxvdF9seSh5ID0gdmluaG9zX2FqdXN0YWRvJGdyYXVfYWxjb2xpY28sIHR5cGUgPSAiYm94IiwgbmFtZSA9ICdUZW9yIEFsY29vbGljbycpDQoNCnN1YnBsb3QoZ3JhZmljb19saXN0YSwgIG5yb3dzID0gMSkNCg0KYGBgYA0KDQpBcMOzcyBhIHJlbW/Dp8OjbyBkbyBlcnJvIG5hIGNvbHVuYSBgZ3JhdV9hbGNvb2xpY29gIG9zIGRhZG9zIHNlIGRlbW9zbnRyYXJhbSBtZWxob3JlcyBkaXNwb3N0b3Mgbm8gZW50YW50byBhaW5kYSBow6EgaW5kaWNpb3MgcXVlIGV4aXN0YW0gb3V0bGllcnMgcXVlIGRldmVtIA0Kc2VyIG1lbGhvciBhbmFsaXNhZG9zDQoNCg0KTyBjb21hbmRvIGFwbGljYWRvIHBhcmEgdG9kb3MgYXRyaWJ1dG9zIGFiYWl4byBleGliacOnw6NvIGRvcyBib3gtcGxvdCBhYmFpeG8gZm9pOiA8YnIgLz4NCg0KPiAqYm94cGxvdChDb2x1bW5fTmFtZSwgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KSAqICA8YnIgLz4NCj4gKm10ZXh0KCJUaXR1bG8iICwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpKg0KDQpgYGB7ciwgLCBlY2hvPUZBTFNFfQ0KDQpwYXIobWZyb3c9YygxLDYpLCBvbWEgPSBjKDEsMSwwLDApICsgMC4xLCAgbWFyID0gYygzLDMsMSwxKSArIDAuMSkNCiAgYm94cGxvdChhY2lkZXpfZml4YSwgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICBtdGV4dCgiQWNpZGV6IEZpeGEiICwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQogIGJveHBsb3QoYWNpZGV6X3ZvbGF0aWwsIGNvbD0ic2xhdGVncmF5MiIsIHBjaD0xOSkNCiAgbXRleHQoIkFjaWRleiBWb2xhdGlsIiwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQogIGJveHBsb3QoYWNpZG9fY2l0cmljbywgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICBtdGV4dCgiYWNpZG9fY2l0cmljbyIsIGNleD0wLjgsIHNpZGU9MSwgbGluZT0yKQ0KICBib3hwbG90KGFjdWNhcl9yZXNpZHVhbCwgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICBtdGV4dCgiYWN1Y2FyX3Jlc2lkdWFsIiwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQogIGJveHBsb3QoY2xvcmV0b3MsIGNvbD0ic2xhdGVncmF5MiIsIHBjaD0xOSkNCiAgbXRleHQoImNsb3JldG9zIiwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQogIGJveHBsb3QoZnNkLCBjb2w9InNsYXRlZ3JheTIiLCBwY2g9MTkpDQogIG10ZXh0KCJmc2QiLCBjZXg9MC44LCBzaWRlPTEsIGxpbmU9MikNCiAgDQoNCmBgYA0KDQoNCg0KYGBge3IsICwgZWNobz1GQUxTRX0NCg0KcGFyKG1mcm93PWMoMSw1KSwgb21hID0gYygxLDEsMCwwKSArIDAuMSwgIG1hciA9IGMoMywzLDEsMSkgKyAwLjEpDQogIGJveHBsb3QodHNkLCBjb2w9InNsYXRlZ3JheTIiLCBwY2g9MTkpDQogIG10ZXh0KCJ0c2QiICwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQogIGJveHBsb3QoZGVuc2lkYWRlLCBjb2w9InNsYXRlZ3JheTIiLCBwY2g9MTkpDQogIG10ZXh0KCJkZW5zaWRhZGUiLCBjZXg9MC44LCBzaWRlPTEsIGxpbmU9MikNCiAgYm94cGxvdChQSCwgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICBtdGV4dCgiUEgiLCBjZXg9MC44LCBzaWRlPTEsIGxpbmU9MikNCiAgYm94cGxvdChzdWxmYXRvcywgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICBtdGV4dCgic3VsZmF0b3MiLCBjZXg9MC44LCBzaWRlPTEsIGxpbmU9MikNCiAgYm94cGxvdChncmF1X2FsY29saWNvLCBjb2w9InNsYXRlZ3JheTIiLCBwY2g9MTkpDQogIG10ZXh0KCJncmF1X2FsY29saWNvIiwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQogIA0KYGBgDQoNCiMjIyAqKk91dGxpZXJzKioNCg0KTm90YSBzZSBxdWUgcXVhc2UgIHRvZGFzIGFzIHZhcmnDoXZlaXMgcG9zc3VlbSBvdXRsaWVycyBlIHBhcmEgdW1hIG1lbGhvciBhY3Vyw6FjaWEgbm8gbW9kZWxvIGlyZW1vcyByZW1vdsOqLWxhcyBkYSBhbsOhbGlzZS4NCk8gTWV0b2RvIG1haXMgaW5kaWNhZG8gcGFyYSByZW1vw6fDo28gZG9zIG91dGxpZXJzIMOpIGFtcGxpdHVkZSBpbnRlcnF1YXJ0aWwgKElRUiAtIEludGVyUXVhbnRpbGUgUmFuZ2UpIG9uZGU6DQogICpJUVIgID0gUTMocXVhcnRpbCAzKSAtIFExKHF1YXJ0aWwgMSkNCkNvbSBvIElRUiBjYWxjdWxhZG8gw6kgbmVjZXNzw6FyaW8gZGVmaW5pciBvIGxpbWl0ZSBpbmZlcmlvciBlIHN1cGVyaW9yIHF1ZSDDqSBkYWRvIHBlbGEgc2VndWludGUgZm9ybXVsYToNCg0KICAkJExpbV97U3VwfSA9IFxiYXJ7WH0gKyAxLDUgICogSVFSJCQNCiAgDQogICQkTGltX3tJbmZ9ID0gXGJhcntYfSAtIDEsNSAgKiBJUVIkJA0KDQpQYXJhIGVsdWNpZGFyIGEgYXBsaWNhw6fDo28gZG8gbcOpdG9kbyB2YW1vcyBhbmFsaXNhciBhIHZhcmlhdmVsICoqIkFjaWRleiBGaXhhIioqIG5vdGFtb3MgcXVlIGjDoSBtdWl0b3MgdmFsb3JlcyBhY2ltYSBkbyBRMyBhIHVucyBwb3Vjb3MgYWJhaXhvIGRlIFExLCBPc2VydmUgbyBzdW3DoXJpbyBkZXN0YSB2YXJpYXZlbA0KDQpgYGB7ciB9DQoNCiAgc2tpbShhY2lkZXpfZml4YSkNCg0KICAjZGFkbyBwMjUoUTEpID0gNi40IGUgcDc1KFEzKSA9IDcuNyB0ZW1vczoNCiAgSVFSX0FjaWRlel9Wb2xhdGlsID03LjcgLSA2LjQNCiAgDQogICNjb21vIHJlc3VsdGFkbyB0ZW1vcyAxLjMgDQogICNjb20gaXNzbyBwb2RlbW9zIGRlZmluaXIgb3MgbGltaXRlcyBzdXBlcmlvciBlIGluZmVyaW9yIGRvIHJhbmdlDQogIA0KICBMU19hY2lkZXpfZml4YSA9IDcuNyArICAxLjUgKiBJUVJfQWNpZGV6X1ZvbGF0aWwNCiAgTElfYWNpZGV6X2ZpeGEgPSA2LjQgLSAoMS41ICogSVFSX0FjaWRlel9Wb2xhdGlsKQ0KICANCiAgI3RlbW9zIHVtIGxpbWl0ZSBzdXBlcmlvciBkZSAtMC4wMjUgZSBpbmZlcmlvciBkZSAwLjY1NSBvbmRlIGFzIG9ic2VydmHDp8O1ZXMgYWNpbWEgb3UgYWJhaXhvIGRlc3NlcyBmYXRvcmVzIHNlcsOjbyBjb25zaWRlcmFkYXMgb3V0aWxlcnMNCiAgI2NvbSBiYXNlIG5vcyBsaW1pdGVzIGVuY29udHJhZG9zIGZhcmVpIHVtIHN1YiBzZXQgZG8gY29uanVudG8gb3JpZ2kgbmFsIGRlIGRhZG9zIGxldmFuZG8gZW0gY29uc2lkZXJhY2FvIG9zIGxpbWl0ZXMgZW5jb250cmFkb3MNCiAgVmluaG9zX05vcm1hbGl6YWRvIDwtZmlsdGVyKHZpbmhvcywgYWNpZGV6X2ZpeGEgPj0gTElfYWNpZGV6X2ZpeGEgJiBhY2lkZXpfZml4YSA8PSBMU19hY2lkZXpfZml4YSkNCiAgDQogICNjb21wYXJhbmRvIG9zIHJlc3VsdGFkb3MNCiAgcGFyKG1mcm93PWMoMSwyKSwgb21hID0gYygxLDEsMCwwKSArIDAuMSwgIG1hciA9IGMoMywzLDEsMSkgKyAwLjEpDQogICAgYm94cGxvdChhY2lkZXpfZml4YSwgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICAgIG10ZXh0KCJBbnRlcyIgLCBjZXg9MC44LCBzaWRlPTEsIGxpbmU9MikNCiAgICBib3hwbG90KFZpbmhvc19Ob3JtYWxpemFkbyRhY2lkZXpfZml4YSwgY29sPSJzbGF0ZWdyYXkyIiwgcGNoPTE5KQ0KICAgIG10ZXh0KCJEZXBvaXMiICwgY2V4PTAuOCwgc2lkZT0xLCBsaW5lPTIpDQoNCmBgYA0KDQpEZSBtb2RvIHF1ZSBhIHJlbW/Dp8OjbyBkb3Mgb3V0bGllcnMgZmV6IGNvbSBxdWUgYSBkaXN0cmlidWnDp8OjbyBkb3MgZGFkb3MgZmljYXNzZSBtYWlzIGhvbW9nw6puZWEgY29tbyBkZW1vbnN0cm8gbm9zIGhpc3Ryb2dyYW1hcyBhIHNlZ3Vpcg0KDQpgYGB7ciB9DQoNCiAgI2NvbXBhcmFuZG8gb3MgcmVzdWx0YWRvcw0KICBwYXIobWZyb3c9YygxLDIpLCBvbWEgPSBjKDEsMSwwLDApICsgMC4xLCAgbWFyID0gYygzLDMsMSwxKSArIDAuMSkNCiAgICBoaXN0KGFjaWRlel9maXhhLCBjb2w9InNsYXRlZ3JheTIiLCBtYWluID0gIiIpDQogICAgbXRleHQoIkFudGVzIiAsIGNleD0wLjgsIHNpZGU9MSwgbGluZT0yKQ0KICAgIGhpc3QoVmluaG9zX05vcm1hbGl6YWRvJGFjaWRlel9maXhhLCBjb2w9InNsYXRlZ3JheTIiLCBtYWluID0gIiIpDQogICAgbXRleHQoIkRlcG9pcyIgLCBjZXg9MC44LCBzaWRlPTEsIGxpbmU9MikNCg0KYGBgDQoNCg0KUGFyYSBhdXRvbWF0aXphciBvIHByb2Nlc3NvIGRlIGFuYWxpc2UgZSByZW1vw6fDo28gZG9zIG91dGxpZXJzIGNyaWFtb3MgdW1hIGZ1bsOnw6NvIHBhcmEgZXhlY3V0YXIgZXNzYSB0YXJlZmEsIGUgc2VndWU6DQoNCmBgYHtyICwgcmVzdWx0cz0iaGlkZSJ9DQoNCg0KDQoNCiNjcmlhbmRvIHVtYSBmdW5jYW8gcGFyYSByZW1vdmVyIG9zIG91dGxpZXJzIGRhcyBjb2x1bmFzIA0KcmVtb3ZlX291dGxpZXJzIDwtIGZ1bmN0aW9uKHgsIG5hLnJtID0gVFJVRSwgLi4uKSB7DQogIHFudCA8LSBxdWFudGlsZSh4LCBwcm9icz1jKC4yNSwgLjc1KSwgbmEucm0gPSBuYS5ybSwgLi4uKQ0KICBIIDwtIDEuNSAqIElRUih4LCBuYS5ybSA9IG5hLnJtKQ0KICB5IDwtIHgNCiAgeVt4IDwgKHFudFsxXSAtIEgpXSA8LSBOQQ0KICB5W3ggPiAocW50WzJdICsgSCldIDwtIE5BDQogIHkNCn0NCg0KdmluaG9zJGFjaWRlel9maXhhIDwtIHJlbW92ZV9vdXRsaWVycyhhY2lkZXpfZml4YSwgVCkNCnZpbmhvcyRhY2lkZXpfdm9sYXRpbCA8LSByZW1vdmVfb3V0bGllcnMoYWNpZGV6X3ZvbGF0aWwpDQp2aW5ob3MkYWNpZG9fY2l0cmljbyA8LSByZW1vdmVfb3V0bGllcnMoYWNpZG9fY2l0cmljbywgVCkNCnZpbmhvcyRhY3VjYXJfcmVzaWR1YWwgPC0gcmVtb3ZlX291dGxpZXJzKGFjdWNhcl9yZXNpZHVhbCwgVCkNCnZpbmhvcyRjbG9yZXRvcyA8LSByZW1vdmVfb3V0bGllcnMoY2xvcmV0b3MsIFQpDQp2aW5ob3MkZnNkIDwtIHJlbW92ZV9vdXRsaWVycyhmc2QsIFQpDQp2aW5ob3MkdHNkIDwtIHJlbW92ZV9vdXRsaWVycyh0c2QsIFQpDQp2aW5ob3MkZGVuc2lkYWRlIDwtIHJlbW92ZV9vdXRsaWVycyhkZW5zaWRhZGUsIFQpDQp2aW5ob3MkUEggPC0gcmVtb3ZlX291dGxpZXJzKFBILCBUKQ0KdmluaG9zJHN1bGZhdG9zIDwtIHJlbW92ZV9vdXRsaWVycyhzdWxmYXRvcywgVCkNCnZpbmhvcyRncmF1X2FsY29saWNvIDwtIHJlbW92ZV9vdXRsaWVycyhncmF1X2FsY29saWNvLCBUKQ0KdmluaG9zJHF1YWxpZGFkZSA8LSByZW1vdmVfb3V0bGllcnMocXVhbGlkYWRlLCBUKQ0KDQpgYGANCg0KRXhhbWluYW5kbyBvcyBkYWRvcyBwb3MgbGltcGV6YSBkb3Mgb3V0bGllcnMNCg0KYGBge3J9DQoNCg0KDQogIA0KYGBgDQo=